﻿using System;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System.IO;
using System.Collections.Generic;
using Nutrapp.DBAL;
using System.Linq;

namespace Nutrapp.Test
{
    //[TestClass]
    public class DataGenerator
    {
        public string[] ingredientNames = new string[12] { "Cucumber", "Broccoli", "Pork", "Lamb", "Beef", "Potato", "Tomato", "Chicken", "Egg", "Onion", "Fish", "Shrimp" }; //, "Potato", "Tomato", "Chicken", "Egg", "Onion", "Fish", "Shrimp", "Carrot", "Salt" 
        public string[] unitName = new string[7] { "g", "kg", "tablespoon", "piece", "liter", "ml", "teaspoon" };
        public string[] categories = new string[4] { "Vegitable", "Meat", "Dairy", "Spice" };
        public string[] pictures = new string[3] {"Potato.jpg", "RoastBeef.jpg", "Pork.jpg"};
        public string[] RecipePrefix = new string[3] { "Fried", "Steamed", "Roasted" };
        public string[] RecipeSufix = new string[3] { "Lasagna", "Sandwiches", "Tacos" };
        public string[] RecipeName = new string[6] { "Cucumber", "Pork", "chicken", "Italian", "Sausage", "tomato" };
        public string[] instructions = new string[5] {
        "Preheat oven to 165 degrees C(325 degrees F ).",
        "In a large mixing bowl, combine the flour and black pepper to taste. Dredge the rump roast in the flour and cover evenly. Shake off excess.",
        "In a large pot over medium/high heat, melt the butter and brown the roast on all sides. Place in a 4 quart casserole dish with lid.",
        "In a small bowl, combine the soup mix, mushroom soup, and vermouth or white wine; pour over roast.",
        "Cover and bake in preheated oven for 3 hours or until desired doneness."};
        public string[] pathsForExpectedResult = new string[7] { 
            @"",
            @"\DesignedRecipeSmallSet\127RecipesExpectedResult.txt",
            @"\DesignedRecipeSmallSet\511RecipesExpectedResult.txt",
            @"\DesignedRecipeSmallSet\1023RecipesExpectedResult.txt",
            @"\DesignedRecipeSmallSet\2047RecipesExpectedResult.txt",
            @"\DesignedRecipeSmallSet\4095RecipesExpectedResult.txt",
            @"\DesignedRecipeSmallSet\8191RecipesExpectedResult.txt",
            };
        public string[] pathsForInfo = new string[7] {
            @"",
            @"\DesignedRecipeSmallSet\InfoFor127Recipes.txt",
            @"\DesignedRecipeSmallSet\InfoFor511Recipes.txt",
            @"\DesignedRecipeSmallSet\InfoFor1023Recipes.txt",
            @"\DesignedRecipeSmallSet\InfoFor2047Recipes.txt",
            @"\DesignedRecipeSmallSet\InfoFor4095Recipes.txt",
            @"\DesignedRecipeSmallSet\InfoFor8191Recipes.txt",
        };
        public string[] pathsForSql = new string[7] { 
            @"\SqlForSmallSet\SqlToSmallSet.sql",
            @"\SqlForSmallSet\127RecipesSql.sql",
            @"\SqlForSmallSet\511RecipesSql.sql",
            @"\SqlForSmallSet\1023RecipesSql.sql",
            @"\SqlForSmallSet\2047RecipesSql.sql",
            @"\SqlForSmallSet\4095RecipesSql.sql",
            @"\SqlForSmallSet\8191RecipesSql.sql",
            };
        //generate sql script for designed recipes with all combination of given ingredients
        [TestMethod]
        public void GenerateSqlForSmallSet()
        {
            List<Unit> units = getUnit();
            List<Ingredients_Category> categories = getCatetory();
            List<Ingredient> ingredients = getIngredient(categories);

            List<string> NameList = new List<string>();
            NameList.AddRange(RecipePrefix);
            NameList.AddRange(RecipeSufix);
            NameList.AddRange(RecipeName);
            List<RecipeNameKeyWord> nameKeyWords = getRecipeNameList(NameList);

            List<Recipe> recipes = getRecipe_with_allCombination_ofIngrdients(ingredients, units, nameKeyWords);
           

            //writeInfoForGenerateData();
            //generateExpectedResult();
           
            // generate sql script for complete small data set

            string useDataBase = "USE [dmae0912_sem4_g1]" + "\n";

            string deleteAllData = "Delete From Recipe_KeyWord" + "\n" +
                                   "Delete From Recipe_Ingredient" + "\n" +
                                   "Delete From Recipe" + "\n" +
                                   "Delete From RecipeNameKeyWord" + "\n" +
                                   "Delete From Ingredients" + "\n" +
                                   "Delete From Ingredients_Category" + "\n" +
                                   "Delete From Unit" + "\n";
            // insert unit
            string insertUnits = "SET IDENTITY_INSERT Unit ON" + "\n";
            for (int i = 0; i < units.Count; i++)
            {
                insertUnits += "INSERT INTO [dbo].[Unit] ([ID], [Unit]) VALUES ('" + units[i].ID + "', '" + units[i].Unit1 + "')" + "\n";
            }

            insertUnits += "SET IDENTITY_INSERT Unit OFF" + "\n";

            //insert category
            string insertCategory = "SET IDENTITY_INSERT Ingredients_Category ON" + "\n";
            for (int i = 0; i < categories.Count; i++)
            {
                insertCategory += "INSERT INTO [dbo].[Ingredients_Category] ([ID], [Name]) VALUES ('" + categories[i].ID + "', '" + categories[i].Name + "')" + "\n";
            }

            insertCategory += "SET IDENTITY_INSERT Ingredients_Category OFF" + "\n";

            //insert ingredient
            string insertIngredient = "SET IDENTITY_INSERT Ingredients ON" + "\n";
            for (int i = 0; i < ingredients.Count; i++)
            {
                insertIngredient += "INSERT INTO [dbo].[Ingredients]([ID],[Name],[Category],[Carbohydrate],[Protein],[Calories],[Fat],[PeiceToGram]) VALUES ('"
                                    + ingredients[i].ID + "', '"
                                    + ingredients[i].Name + "', '"
                                    + ingredients[i].Category + "', '"
                                    + ingredients[i].Category + "', '"
                                    + ingredients[i].Protein + "', '"
                                    + ingredients[i].Category + "', '"
                                    + ingredients[i].Fat + "', '"
                                    + ingredients[i].PeiceToGram + "')" + "\n";

            }
            insertIngredient += "SET IDENTITY_INSERT Ingredients OFF" + "\n";

            //insert recipe name key words: prefix, sufix, name
            string insertRecipeName = "SET IDENTITY_INSERT RecipeNameKeyWord ON" + "\n";

            for (int i = 0; i < nameKeyWords.Count; i++)
            {
                insertRecipeName += "INSERT INTO [dbo].[RecipeNameKeyWord] ([ID], [KeyWord]) VALUES('"
                                    + nameKeyWords[i].ID + "', '"
                                    + nameKeyWords[i].KeyWord + "')" + "\n";
            }
            insertRecipeName += "SET IDENTITY_INSERT RecipeNameKeyWord OFF" + "\n";
            //insert recipe with all comboes of ingredients
            string insertRecipe = "SET IDENTITY_INSERT Recipe ON" + "\n";
            for (int i = 0; i < recipes.Count; i++)
            {
                insertRecipe += "INSERT INTO [dbo].[Recipe] ([ID], [Name] ,[Cost] ,[Rating] ,[Instruction] ,[Picture] ,[Carbohydrate] ,[Protein] ,[Calories] ,[Fat]) VALUES ('"
                                + recipes[i].ID + "', '"
                                + recipes[i].Name + "', '"
                                + recipes[i].Cost + "', '"
                                + recipes[i].Rating + "', '"
                                + recipes[i].Instruction + "', '"
                                + recipes[i].Picture + "', '"
                                + recipes[i].Carbohydrate + "', '"
                                + recipes[i].Protein + "', '"
                                + recipes[i].Calories + "', '"
                                + recipes[i].Fat + "')" + "\n";
            }
            insertRecipe += "SET IDENTITY_INSERT Recipe OFF" + "\n";

            string insertRecipeNameKeyWords = "";
            for (int i = 0; i < recipes.Count; i++)
            {
                for (int j = 0; j < recipes[i].Recipe_KeyWords.Count; j++)
                {
                    insertRecipeNameKeyWords += "INSERT INTO [dbo].[Recipe_KeyWord]([RID],[KID]) VALUES ('"
                                                + recipes[i].Recipe_KeyWords[j].RID + "', '"
                                                + recipes[i].Recipe_KeyWords[j].KID + "')" + "\n";
                }
            }

            string insertIngredientLines = "";
            for (int i = 0; i < recipes.Count; i++)
            {
                for (int j = 0; j < recipes[i].Recipe_Ingredients.Count; j++)
                {
                    insertIngredientLines += "INSERT INTO [dbo].[Recipe_Ingredient] ([RID],[IID],[Quantity] ,[UID]) VALUES ('"
                                             + recipes[i].Recipe_Ingredients[j].RID + "', '"
                                             + recipes[i].Recipe_Ingredients[j].IID + "', '"
                                             + recipes[i].Recipe_Ingredients[j].Quantity + "', '"
                                             + recipes[i].Recipe_Ingredients[j].UID + "')" + "\n";
                }
            }

            

            // add all scripts from different part to one 
            string sqlScript = useDataBase + deleteAllData + insertUnits + insertCategory + insertIngredient + insertRecipeName + insertRecipe + insertRecipeNameKeyWords + insertIngredientLines;
            writeSqlToFile(sqlScript);
            
        }

        // export sql script string to a document
        public void writeSqlToFile(string sql)
        {
            string path = IOHelper.getPathForTestDataSearchEngine() + pathsForSql[5];
            StreamWriter file = new StreamWriter(path);

            file.WriteLine(sql);

            file.Close();
        }

        // generate search result statistics for recipe input
        public void generateExpectedResult()
        {
            string path = IOHelper.getPathForTestDataSearchEngine() + @"\DesignedRecipeSmallSet\ExpectedResultForBigSet.txt";
            StreamWriter file = new StreamWriter(path);

            // write expected results
            file.WriteLine("strict search for ingredients");
            file.WriteLine("ingredient" + "\t" +"recipe");
            string strictInfo = "";
            for (int i = 0; i < ingredientNames.Count(); i++)
            {
                strictInfo += (i + 1) + "\t" + MathHelper.get_allComboes_contains_N_elements_inList(ingredientNames.Count(), i + 1) + "\n";
            }

            file.Write(strictInfo);
            file.WriteLine("loose search for ingredients");
            file.WriteLine("ingredient" + "\t" + "recipe");

            string looseInfo = "";
            for (int i = 0; i < ingredientNames.Count(); i++)
            {
                looseInfo += (i + 1) + "\t" + MathHelper.get_allComboes_consist_atLeast_oneOf_N_elenments_inList(ingredientNames.Count(), i + 1) +"\n";
            }

            file.Write(looseInfo);
            file.Close();
        }

        public void writeInfoForGenerateData()
        {
            string path = IOHelper.getPathForTestDataSearchEngine() + @"\DesignedRecipeSmallSet\infoForBigSet.txt";
            StreamWriter file = new StreamWriter(path);

            //write expected results

            file.WriteLine("Ingredient" + "\t" + ingredientNames.Count());
            file.WriteLine("RecipeNamePrefix" + "\t" + RecipePrefix.Count());
            file.WriteLine("RecipeNamesufix" + "\t" + RecipeSufix.Count());
            file.WriteLine("RecipeName" + "\t" + RecipeName.Count());
            file.WriteLine("Ingredient_Category" + "\t" + categories.Count());
            file.WriteLine("Unit" + "\t" + unitName.Count());
            file.WriteLine("Instruction" + "\t" + instructions.Count());

            file.Close();
        }

        
        //generate an unit list based on given unit names
        public List<Unit> getUnit()
        {

            List<Unit> units = new List<Unit>();
            for (int i = 0; i < unitName.Length; i++)
            {
                string Name = unitName[i];
                Unit u = new Unit() { ID = i + 1, Unit1 = Name };
                units.Add(u);
            }
            return units;

        }

        //generate an category list based on given category names
        
        public List<Ingredients_Category> getCatetory()
        {
            List<Ingredients_Category> category = new List<Ingredients_Category>();

            for (int i = 0; i < categories.Length; i++)
            {
                string categoryName = categories[i];
                Ingredients_Category ic = new Ingredients_Category() { ID = i + 1, Name = categoryName };
                category.Add(ic);
            }

            return category;

        }


        //generate an ingredient list based on given ingredient names
        public List<Ingredient> getIngredient(List<Ingredients_Category> categories)
        {

            List<Ingredient> ingredients = new List<Ingredient>();
            List<Ingredients_Category> category = categories;
            Random r = new Random();
            int categorySize = category.Count;

            for (int i = 0; i < ingredientNames.Length; i++)
            {
                string ingredientName = ingredientNames[i];
                Ingredient ingredient = new Ingredient() { ID = i + 1, Name = ingredientName, Category = r.Next(1, categorySize), Protein = r.Next(1, 100), Carbohydrate = r.Next(1, 100), Fat = r.Next(1, 100), Calories = r.Next(1, 100), PeiceToGram = r.Next(1, 30) };
                ingredients.Add(ingredient);
            }

            return ingredients;

        }

        public List<RecipeNameKeyWord> getRecipeNameList(List<string> nameList)
        {
            List<RecipeNameKeyWord> rkw = new List<RecipeNameKeyWord>();
            for (int i = 0; i < nameList.Count; i++)
            {
                RecipeNameKeyWord kw = new RecipeNameKeyWord() { ID = i+1, KeyWord = nameList[i].ToLower() };
                rkw.Add(kw);
            }
            return rkw;
        }

        
        //return all recipes based on all combinations of given ingredients list
        public List<Recipe> getRecipe_with_allCombination_ofIngrdients(List<Ingredient> ingredient, List<Unit> units, List<RecipeNameKeyWord> namelist)
        {
            Random r = new Random();
            int subfixSize = RecipeSufix.Length;

            List<Ingredient> ingredients = ingredient;
            int ingredientsSize = ingredient.Count;
            List<List<Ingredient>> allCombinationOfIngredients = getAllCombinationOfIngredients(ingredients, new List<Ingredient>());
            List<Recipe> RecipeList = new List<Recipe>();

            //generate all recipes with all comboes of ingredients
            for (int i = 1; i <= allCombinationOfIngredients.Count; i++)
            {

                Recipe recipe = new Recipe()
                {
                    ID = i,
                    Cost = r.Next(1, 100),
                    Rating = r.Next(1, 5),
                    Protein = r.Next(1, 40),
                    Calories = r.Next(10, 400),
                    Picture = pictures[r.Next(0, 2)],
                    Instruction = instructions[r.Next(0, 4)] + instructions[r.Next(0, 4)],
                    Carbohydrate = r.Next(1, 40),
                    Fat = r.Next(1, 40)
                };
                string recipeName; //pick a random recipe name with either prefix or sufix
                if (r.Next(1, 100) < 50)
                {
                    recipeName = RecipePrefix[r.Next(0, RecipePrefix.Length)].Trim().ToLower() + " " + RecipeName[r.Next(0, RecipeName.Length)].Trim().ToLower();
                    recipe.Name = recipeName;
                }
                else
                {
                    recipeName = RecipeName[r.Next(0, RecipeName.Length)].Trim().ToLower() + " " + RecipeSufix[r.Next(0, RecipeSufix.Length)].Trim().ToLower();
                }
                recipe.Name = recipeName;

                //insert key words for recipe
                List<Recipe_KeyWord> keyWords = new List<Recipe_KeyWord>();
                string[] inputname = recipeName.Split(' ');
                for (int k = 0; k < inputname.Length; k++)
                {
                    RecipeNameKeyWord theName = namelist.FirstOrDefault(l => l.KeyWord.Equals(inputname[k]));
                    Recipe_KeyWord kw = new Recipe_KeyWord() { RID = recipe.ID, KID = theName.ID};
                    keyWords.Add(kw);
                }
                recipe.Recipe_KeyWords.AddRange(keyWords);

                //insert ingredient lines for recipe
                List<Recipe_Ingredient> ingredientLine = new List<Recipe_Ingredient>();
                List<Ingredient> ingredientList = allCombinationOfIngredients[i - 1];
                for (int j = 1; j <= ingredientList.Count; j++)
                {
                    Recipe_Ingredient line = new Recipe_Ingredient()
                    {
                        IID = ingredientList[j - 1].ID,
                        RID = recipe.ID,
                        UID = r.Next(1, units.Count),
                        Quantity = r.Next(1, 10)
                    };
                    ingredientLine.Add(line);
                }
                recipe.Recipe_Ingredients.AddRange(ingredientLine);
                RecipeList.Add(recipe);
            }
            return RecipeList;



        }

        // return the all possible combinations of n size ingredients
        // fx 3 ingredients {a, b, c} should return 7 combinations: {a}, {b}, {c}, {a, b}, {b, c}, {a, c}, {a, b, c}
        public List<List<Ingredient>> getAllCombinationOfIngredients(List<Ingredient> ingredients, List<Ingredient> currentCombo)
        {
            List<Ingredient> restOfIngredient = ingredients.ToList();
            List<List<Ingredient>> result = new List<List<Ingredient>>();
            if (ingredients.Count != 0)
            {
                foreach (var ingredient in ingredients.ToList())
                {

                    restOfIngredient.Remove(ingredient);
                    List<Ingredient> newCombo = currentCombo.ToList();
                    newCombo.Add(ingredient);
                    result.Add(newCombo);
                    result.AddRange(getAllCombinationOfIngredients(restOfIngredient, newCombo));

                }

                
            }
            return result;
        }




    }
}
